package com.minli.weixin.common.util;

import com.minli.weixin.common.util.entity.HttpReq;
import com.minli.weixin.common.util.entity.HttpResp;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.*;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 * <p>http工具类</p>
 * <p>Created by qrf on 2016/8/12.</p>
 */
public class HttpUtils {

    private static String              DEFAULT_CHARSET                     = "GBK";

    /** 连接超时时间，由bean factory设置，缺省为8秒钟 */
    private int                        defaultConnectionTimeout            = 8000;

    /** 回应超时时间, 由bean factory设置，缺省为30秒钟 */
    private int                        defaultSoTimeout                    = 30000;

    /** 闲置连接超时时间, 由bean factory设置，缺省为60秒钟 */
    private int                        defaultIdleConnTimeout              = 60000;

    private int                        defaultMaxConnPerHost               = 30;

    private int                        defaultMaxTotalConn                 = 80;

    /** 默认等待HttpConnectionManager返回连接超时（只有在达到最大连接数时起作用）：1秒*/
    private static final long          defaultHttpConnectionManagerTimeout = 3 * 1000;

    /**
     * HTTP连接管理器，该连接管理器必须是线程安全的.
     */
    private HttpConnectionManager      connectionManager;

    private static HttpUtils httpUtils = new HttpUtils();

    /**
     * 工厂方法
     * 
     * @return 单实例
     */
    public static HttpUtils getInstance() {
        return httpUtils;
    }

    /**
     * 私有的构造方法
     */
    private HttpUtils() {
        // 创建一个线程安全的HTTP连接池
        connectionManager = new MultiThreadedHttpConnectionManager();
        connectionManager.getParams().setDefaultMaxConnectionsPerHost(defaultMaxConnPerHost);
        connectionManager.getParams().setMaxTotalConnections(defaultMaxTotalConn);

        IdleConnectionTimeoutThread ict = new IdleConnectionTimeoutThread();
        ict.addConnectionManager(connectionManager);
        ict.setConnectionTimeout(defaultIdleConnTimeout);

        ict.start();
    }

    /**
     * 获取实例
     * @return
     */
    private HttpClient getHttpClient(){
        HttpClient httpclient = new HttpClient(connectionManager);
        // 设置连接超时
        httpclient.getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout);

        // 设置回应超时
        httpclient.getHttpConnectionManager().getParams().setSoTimeout(defaultSoTimeout);

        // 设置等待ConnectionManager释放connection的时间
        httpclient.getParams().setConnectionManagerTimeout(defaultHttpConnectionManagerTimeout);
        return httpclient;
    }

    /**
     * get 请求
     * @param url
     * @return
     */
    public HttpResp get(String url){
        HttpReq httpReq = new HttpReq(url);
        return get(httpReq);
    }

    /**
     * get 请求
     * @param httpReq
     * @return
     */
    public HttpResp get(HttpReq httpReq){
        GetMethod method = new GetMethod(httpReq.getUrl());
        method.addRequestHeader("User-Agent", "Mozilla/4.0");
        method.getParams().setCredentialCharset(DEFAULT_CHARSET);
        try {
            int status = getHttpClient().executeMethod(method);
            HttpResp httpResp = new HttpResp();
            httpResp.setStatus(status);
            httpResp.setHeaders(method.getResponseHeaders());
            httpResp.setBodyByte(method.getResponseBody());
            return httpResp;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            method.releaseConnection();
        }
        return null;
    }

    /**
     * post 请求
     * @param url
     * @param params
     * @return
     */
    public HttpResp post(String url,Map<String,String> params){
        HttpReq httpReq = new HttpReq(url);
        httpReq.setParams(params);
        return post(httpReq);
    }

    /**
     * post 请求
     * @param httpReq
     * @return
     */
    public HttpResp post(HttpReq httpReq){
        PostMethod method = new PostMethod(httpReq.getUrl());
        method.addRequestHeader("User-Agent", "Mozilla/4.0");
        try {
            if(httpReq.getFiles() == null){
                method.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; text/html; charset=" + DEFAULT_CHARSET);
                method.addParameters(mapToNameValuePairs(httpReq.getParams()));
            }else{
                List<Part> parts = new ArrayList<Part>();
                for (String key:httpReq.getParams().keySet()) {
                    parts.add(new StringPart(key, httpReq.getParams().get(key), DEFAULT_CHARSET));
                }
                for(File file:httpReq.getFiles()){
                    //增加文件参数，strParaFileName是参数名，使用本地文件
                    parts.add(new FilePart(file.getName(), new FilePartSource(file)));
                }
                // 设置请求体
                method.setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[0]), new HttpMethodParams()));
            }

            int status = getHttpClient().executeMethod(method);
            HttpResp httpResp = new HttpResp();
            httpResp.setStatus(status);
            httpResp.setHeaders(method.getResponseHeaders());
            httpResp.setBodyByte(method.getResponseBody());
            return httpResp;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            method.releaseConnection();
        }
        return null;
    }

    /**
     * MAP类型数组转换成NameValuePair类型
     * @param properties  MAP类型数组
     * @return NameValuePair类型数组
     */
    private static NameValuePair[] mapToNameValuePairs(Map<String, String> properties) {
        NameValuePair[] nameValuePair = new NameValuePair[properties.size()];
        int i = 0;
        for (Map.Entry<String, String> entry : properties.entrySet()) {
            nameValuePair[i++] = new NameValuePair(entry.getKey(), entry.getValue());
        }

        return nameValuePair;
    }
    
    /**
     * 流形式发送GET或POST请求
     * @param requestUrl
     * @param requestMethod
     * @param outputStr
     * @return
     */
    public String httpRequest(String requestUrl, String requestMethod, String outputStr){
    	HttpsURLConnection conn = null;
    	OutputStream outputStream = null;
    	InputStream inputStream = null;
    	try {
            URL url = new URL(requestUrl);
            conn = (HttpsURLConnection) url.openConnection();
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setConnectTimeout(defaultSoTimeout);
            // 设置请求方式（GET/POST）
            conn.setRequestMethod(requestMethod);
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 从输入流读取返回内容
            inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            return buffer.toString();
        } catch (ConnectException ce) {
        	ce.printStackTrace();
        } catch (Exception e) {
        	e.printStackTrace();
        }finally{
        	try{
                if(inputStream!=null){
                	inputStream.close();
                }
                if(conn!=null){
                	conn.disconnect();
                }
            }catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return null;
    }
}
